package com.skit.common.http

import android.content.Context
import android.net.ConnectivityManager
import android.util.Log
import cn.bmob.v3.datatype.BmobFile
import cn.bmob.v3.exception.BmobException
import com.skit.common.R
import com.skit.common.ResourcesManager
import com.skit.common.bean.StatusResult
import com.skit.common.manager.UserManager
import com.skit.common.utils.ToastUtils
import okhttp3.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit

/**
 * @author shuike [Contact me.](mailto:shuike007@gmail.com)
 * @version 1.0
 * @since 2020/5/20
 */
object HttpUtils {
    private const val TAG = "HttpUtils"

    @JvmStatic
    var okHttpClient: OkHttpClient? = null
        get() {
            if (field == null) {
                synchronized(HttpUtils::class.java) {
                    if (field == null) {
                        field = OkHttpClient.Builder()
                            .connectTimeout(10, TimeUnit.SECONDS)
                            .readTimeout(10, TimeUnit.SECONDS)
                            .callTimeout(10, TimeUnit.SECONDS)
                            .writeTimeout(10, TimeUnit.SECONDS)
//                            .cache(
//                                Cache(
//                                    File(
//                                        ResourcesManager.getAppContext().cacheDir,
//                                        "okHttpCache"
//                                    ), 10 * 1024 * 1024
//                                )
//                            )
                            .addNetworkInterceptor(TokenInterceptor())//添加拦截器
                            .addInterceptor(LogInterceptor())
//                            .addInterceptor(CacheInterceptor())//添加拦截器
                            .build()
                    }
                }
            }
            return field
        }
        private set

    class LogInterceptor : Interceptor {

        companion object {
            private const val METHOD_GET = "GET"
            private const val METHOD_POST = "POST"
        }

        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
            val request: Request = chain.request()
            val requestBuilder: Request.Builder = request.newBuilder()
            val urlBuilder: HttpUrl.Builder = request.url.newBuilder()
            val sb = StringBuilder()
            val params = StringBuilder()
            val t1 = System.nanoTime() //请求发起的时间
            sb.append(String.format("请求地址👉 %s%n请求方式 = %s 👈%n", request.url, request.method))
            if (METHOD_GET == request.method) { // GET方法
                // 这里可以添加一些公共get参数
                val httpUrl: HttpUrl = urlBuilder.build()
                // 打印所有get参数
                val paramKeys = httpUrl.queryParameterNames
                for (key in paramKeys) {
                    val value = httpUrl.queryParameter(key)
                    params.append(String.format("👉%s= %s%n", key, value))
                }
                // 将最终的url填充到request中
                requestBuilder.url(httpUrl)
            } else if (METHOD_POST == request.method) { // POST方法
                val bodyBuilder = FormBody.Builder()
                if (request.body is FormBody) {
                    val formBody = request.body as FormBody?
                    for (i in 0 until formBody!!.size) {
                        bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i))
                    }

                    // 这里可以添加一些公共post参数
                    val newBody: FormBody = bodyBuilder.build()
                    // 打印所有post参数
                    for (i in 0 until newBody.size) {
                        params.append(
                            String.format(
                                "👉%s= %s%n",
                                newBody.name(i),
                                newBody.value(i)
                            )
                        )
                    }
                    // 将最终的表单body填充到request中
                    requestBuilder.post(newBody)
                } else {

                }
            }
            sb.append(String.format("【request请求参数👇】%n%s%n", params))
            val headerString = StringBuilder()
            request.headers.map {
                headerString.append(it.first + ":" + it.second)
            }
            sb.append(String.format("【header参数\uD83D\uDC47】%n%s%n", headerString))
            val response: okhttp3.Response = chain.proceed(requestBuilder.build())

            //这里不能直接使用response.body().string()的方式输出日志
            //因为response.body().string()之后，response中的流会被关闭，程序会报错，我们需要创建出一个新的response给应用层处理
            val responseBody = response.peekBody((1024 * 1024).toLong())
            val mediaType = response.body!!.contentType()
            val t2 = System.nanoTime() //收到响应的时间
            sb.append(String.format("【code🏠】%s👈%n", response.code))
            sb.append(String.format("【MediaType】%s%n", mediaType.toString()))
            sb.append(String.format("【response返回值】%s%n", responseBody.string()))
            sb.append(String.format("【 ==== 总耗时👉:【%.1fms】👈==== 】", (t2 - t1) / 1e6))
            Log.d(TAG, "intercept: $sb")
            return response
        }

    }

    class TokenInterceptor : Interceptor {
        override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
            val request = chain.request()
            val user = UserManager.user
            return if (user == null || user.token.isEmpty()) {
                chain.proceed(request)
            } else {
                Log.d(TAG, "intercept token: ${user.token}")
                val build =
                    request.newBuilder().header("token", user.token).build()
                chain.proceed(build)
            }
        }
    }

    fun isNetworkConnected(context: Context?): Boolean {
        val mConnectivityManager =
            context?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val mNetworkInfo = mConnectivityManager.activeNetworkInfo
        return mNetworkInfo != null && mNetworkInfo.isConnected
    }

    class CacheInterceptor : Interceptor {
        override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
            val request = chain.request()
            val user = UserManager.user
            val build =
                request.newBuilder().apply {
                    header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60)
                    if (isNetworkConnected(ResourcesManager.getAppContext())) {
                        // 无网络时，强制使用缓存
                        cacheControl(CacheControl.FORCE_CACHE)
                    }
                }.build()


            return chain.proceed(build)
        }
    }

    @JvmStatic
    fun <T> doRequest(
        call: Call<StatusResult<T>>,
        callback: RequestFinishCallback<T>?
    ) {
        call.enqueue(object : Callback<StatusResult<T>?> {
            override fun onResponse(
                call: Call<StatusResult<T>?>,
                response: Response<StatusResult<T>?>
            ) {
                val result = response.body()
                Log.d(
                    TAG,
                    "onResponse: $response"
                )
                if (null != callback && null != result) {
                    Log.d(TAG, result.msg)
                    callback.getRequest(result)
                }
            }

            override fun onFailure(
                call: Call<StatusResult<T>?>,
                t: Throwable
            ) {
                t.printStackTrace()
                Log.d(TAG, "onFailure: ")
                ToastUtils.showToast(R.string.server_error)
                callback?.getRequest(null)
            }
        })
    }

    @JvmStatic
    fun <T> doRequest(
        call: Call<StatusResult<T>>,
        callback: (StatusResult<T>?) -> Unit
    ) {
        doRequest(
            call,
            object : RequestFinishCallback<T> {
                override fun getRequest(result: StatusResult<T>?) {
                    callback(result)
                }
            })
    }

    fun uploadFile(
        path: String,
        uploadFileListener: UploadFileListener
    ) {
        val bmobFile = BmobFile(File(path))

        bmobFile.uploadblock(object : cn.bmob.v3.listener.UploadFileListener() {
            override fun done(e: BmobException?) {
                if (e == null) {
                    uploadFileListener.done(bmobFile.fileUrl)
                } else {
                    uploadFileListener.done(null)
                }
            }
        })
    }

    interface RequestFinishCallback<T> {
        fun getRequest(result: StatusResult<T>?)
    }
}